#include <iostream>
#include "global.h"

int global_i = 10;

int main() {
    {
        int a = 10;
        const int b = a;            // a = 10, b = 10

        a = 20;                     // a = 20, b = 10

        int c = b;                  // c = 10, b = 10

        c = 30;                     // c = 30, b = 10
    }

    {
        std::cout << "main.cpp bufSize: " << &bufSize << std::endl;
        std::cout << "main.cpp bufSize2: " << &bufSize2 << std::endl;
        printN();
    }

    {
        int aa = 10;
        const int ca = 20;
        const int &r1 = aa;
        // int &r2 = ca; 错误
        const int &r2 = ca;         // aa = 10, ca = 20, r1 = 10, r2 = 20
    }

    {
        double pi = 3.14;
        const int &r3 = pi;         // 编译器做了下面的优化

        const int &temp = pi;
        const int &r4 = temp;
    }

    {
        int m = 1024;
        const int &r5 = m;
        int &r6 = m;
        r6 = 2048;                  // 都是2048，但是r5不能主动改
        std::cout << m << ' ' << r5 << ' ' << r6 << std::endl;
    }

    {
        // const 右边 不能改

        int a = 10, b = 20;
        const int *p1 = &a;                         // *p1不能改
        //*p1 = 20;                                    错误，p1所指向的内存中的值不可修改
        p1 = &b;                                    // p1的指向是可以修改的

        int c = 10, d = 20;
        int * const p2 = &c;                        // p2不能改
        // p2 = &d;                                     错误，p2指向的内存地址不可修改
        *p2 = 20;

        const int * const p3 = &c;                  // 指向无法修改，值也无法修改


        // 赋初值问题
        int k = 0;
        int * const p4 = &k;                         // p4本身是无法修改的，再不赋初值，编译器就不允许了
        *p4 = 10;
        const int *p5;                              // p5 的指向是可以修改的，但是值是无法修改的，所以是可以不赋初值的
        p5 = &k;
    }

    {
        // 顶层指针和底层指针
        // 顶层指针表示指针本身是一个常量，底层指针表示指针指向的内容是一个常量
        // 所以上面代码段中，p1是底层指针，p2是顶层指针，p3两个都是，p4顶层指针，p5是底层指针

        const int ci = 42;  // 不能修改ci的值，底层const
        const int &r = ci;  // 不能修改r所指向的（ci）的值，底层const

        int i = 40;
        const int &r1 = i;  // 不能通过r1修改i的值，底层const


        int errNum = 0;
        const int *p = &errNum;
        // int *p2 = p;         p是底层const，他所指向的值是无法修改的，他的值是可以赋值给p2的，但是p2可以修改p所指向的值，这是不对的，所以编译器不允许
    }

    {
        // 编译阶段就知道的，才是常量表达式，否则不是
        const int max_files = 20;       // 常量表达式
        const int limit = max_files + 10;   // 常量表达式
        int staff_size = 20;            // 非常量表达式，无const声明
        const int sz = GetSize();       // 运行阶段才知道GetSize()是多少，虽然是常量，但是编译阶段不知道，所以不是常量表达式
    }

    {
        // constexpr int i = GetSize();   // 错误，GetSize不是一个常量表达式，不能用constexpr声明，编译阶段直接报错
        constexpr int sz = GetSizeConst(); // GetSizeConst()，是一个常量表达式，所以可以用constexpr声明，不会报错
    }

    {
        const int *p = nullptr;         // p是一个指向整型常量的指针
        constexpr int *q = nullptr;     // q是一个指向整数的常量指针，等价于 int * const q = nullptr
        // q ++ ; q = &k;               错误，不能修改q本身，但是可以修改指向的值，但是运行时就可能会报错 *q = 10;

        // 一个constexpr指针的初始值必须是nullptr或者是0，或者是存储于某个固定地址中的对象（全局变量、全局常量）
        constexpr int *p1 = nullptr;
        constexpr int *p2 = &global_i;   // 绑定全局变量，全局变量地址固定
        *p2 = 1024;                     // 可以修改p2指向的内容
    }

    {
        constexpr const int *cp = &global_i;    // 两种写法等价，指向不能改，指向的内容也不能改
        const int * const cp2 = &global_i;
        // cp ++ ; *cp = 1024;                  // 都是错误的，无法修改
        // cp2 ++ ; *cp2 = 1024;
    }

    return 0;
}